package com.tea.modules.java8.thread.completable;

import java.util.concurrent.CompletableFuture;

/**
 * com.tea.modules.java8.thread.completable
 *
 * @author jaymin
 * @since 2022/4/24
 */
public class CompletableExceptions {
    static CompletableFuture<Breakable> test(String id, int failcount) {
        return CompletableFuture.completedFuture(
                        new Breakable(id, failcount))
                .thenApply(Breakable::work)
                .thenApply(Breakable::work)
                .thenApply(Breakable::work)
                .thenApply(Breakable::work);
    }

    /**
     * 测试 A 到 E 运行到抛出异常，然后…并没有将抛出的异常暴露给调用方。 <br>
     * 只有在测试 F 中调用 get() 时，我们才会看到抛出的异常。  <br>
     * 测试 G 表明，你可以首先检查在处理期间是否抛出异常，而不抛出该异常。 <br>
     * 然而，test H 告诉我们，不管异常是否成功，它仍然被视为已“完成”。  <br>
     * 代码的最后一部分展示了如何将异常插入到 CompletableFuture 中，而不管是否存在任何失败。  <br>
     * 在连接或获取结果时，我们使用 CompletableFuture 提供的更复杂的机制来自动响应异常，而不是使用粗糙的 try-catch。 <br>
     * 你可以使用与我们看到的所有 CompletableFuture 相同的表单来完成此操作:在链中插入一个 CompletableFuture 调用。 <br>
     * 有三个选项 exceptionally()，handle()， whenComplete(): <br>
     *
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(" Exceptions don't appear ...................................................");
        // Exceptions don't appear ...
        test("A", 1);
        test("B", 2);
        test("C", 3);
        test("D", 4);
        test("E", 5);
        System.out.println("until you try to fetch the value..............................................");
        // ... until you try to fetch the value:
        try {
            test("F", 2).get(); // or join()
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        System.out.println("Test for exceptions................................................................");
        // Test for exceptions:
        System.out.println(
                test("G", 2).isCompletedExceptionally()
        );
        // Counts as "done":
        System.out.println(test("H", 2).isDone());
        // Force an exception:
        CompletableFuture<Integer> cfi =
                new CompletableFuture<>();
        System.out.println("done? " + cfi.isDone());
        cfi.completeExceptionally(new RuntimeException("forced"));
        try {
            cfi.get();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        System.out.println("**** Failure Mode ****");
        handleException(2);
        System.out.println("**** Success Mode ****");
        handleException(0);
    }


    /**
     * exceptionally() 参数仅在出现异常时才运行。exceptionally() 局限性在于，该函数只能返回输入类型相同的值。 <br>
     *
     * exceptionally() 通过将一个好的对象插入到流中来恢复到一个可行的状态。 <br>
     *
     * handle() 一致被调用来查看是否发生异常（必须检查 fail 是否为 true）。<br>
     *
     * 但是 handle() 可以生成任何新类型，所以它允许执行处理，而不是像使用 exceptionally()那样简单地恢复。<br>
     *
     * whenComplete() 类似于 handle()，同样必须测试它是否失败，但是参数是一个消费者，并且不修改传递给它的结果对象。<br>
     * @param failCount
     */
    static void handleException(int failCount) {
        // Call the Function only if there's an
        // exception, must produce same type as came in:
        CompletableExceptions
                .test("exceptionally", failCount)
                // Function
                .exceptionally((ex) -> {
                    if (ex == null) {
                        System.out.println("无事发生");
                    }
                    return new Breakable(ex.getMessage(), 0);
                })
                .thenAccept(str -> System.out.println("result: " + str));

        // Create a new result (recover):
        CompletableExceptions
                .test("handle", failCount)
                // BiFunction
                .handle((result, fail) -> {
                    if (fail != null) {
                        return "故障恢复对象";
                    } else {
                        return result + " 运行正常";
                    }
                })
                .thenAccept(str -> System.out.println("result: " + str));

        // Do something but pass the same result through:
        CompletableExceptions
                .test("whenComplete", failCount)
                // BiConsumer
                .whenComplete((result, fail) -> {
                    if (fail != null) {
                        System.out.println("坏事了");
                    } else {
                        System.out.println(result + " OK");
                    }
                })
                .thenAccept(r -> System.out.println("result: " + r));
    }

}
